/**
 * \file Expression.cpp
 * \brief Implementation of expression-oriented Code subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#include <sys/stat.h>
#include <iostream>
#include "fdstream.hpp"

#include <aterm1.h>

#include "Expression.h"
#include "Value.h"
#include "Symbol.h"
#include "Declaration.h"
#include "Code.h"
#include "BinaryOp.h"
#include "parse.h"
#include "debug.h"
#include "foreign.h"

Value *Address::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  LeftExpression *route = data->buildStoragePath(symbols, out);

  std::vector<PointerOf*> povect;
  povect.push_back(new PointerOf());
  type = constructType(route->type, povect, symbols);
  delete povect[0];

  // *** TODO: SHOULD WORK WITH MULTIPLE IDs ***
  Id *id = route->findId(symbols);
  if (!id)
  {
    plog.warning() << HDR << "cannot take address of " << route->toString() << std::endl;
    return new PointerValue();
  }

  Variable *var = dynamic_cast<Variable*>(symbols->findSymbol(id->name));
  if (!var)
  {
    plog.warning() << HDR << "taking address of " << id->name << " without declaration"
	      << std::endl;
    return new PointerValue();
  }

  return new PointerValue(symbols, var->index, route);
}

void Address::residualize(SymbolTable *symbols)
{
  data->residualize(symbols);
}

std::string Address::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "&(" << data->toString() << ")");
}

std::string Address::toTerm()
{
  return (char*)(CStr("Address(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *TypeCast::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  delete tname->reduce(symbols, out);
  type = tname->type;

  Expression *res = data->reduce(symbols, out, use);

  if (res->known)
  {
    Value *val = dynamic_cast<Value*>(res);
    
    if ((dynamic_cast<IntegerValue*>(val) || dynamic_cast<FloatValue*>(val)) &&
	(typeid(*type) == typeid(IntegerType) || typeid(*type) == typeid(FloatType)))
      return type->makeValue(val->castToFloat());
  }

  if (type->equals(res->type))
    return res;

  return new TypeCast(type, tname->copy(), res);
}

void TypeCast::residualize(SymbolTable *symbols)
{
  data->residualize(symbols);
}

std::string TypeCast::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "(" << tname->toString() << ") "
		 << data->toString());
}

std::string TypeCast::toTerm()
{
  return (char*)(CStr("TypeCast(") << tname->toTerm() << ", " << data->toTerm() << ")");
}

/* ************************************************************************* */

IntegerValue *SizeofExp::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  Type *dtype;

  type = &BasicInteger;

  LeftExpression *lexp = dynamic_cast<LeftExpression*>(data);
  
  if (lexp)
  {
    std::list<Value**> storage, path;

    lexp = lexp->buildStoragePath(symbols, out);
    lexp->findStorage(symbols, out, storage, path);

    dtype = lexp->type;

    if (storage.size() == 1)
    {
      CodeValue *code = dynamic_cast<CodeValue*>(*storage.front());

      if (code)
      {
	delete code->data->reduce(symbols, out);
	dtype = code->data->type;

	if (dtype == &Unknown)
	{
	  /* If we can't find the type for an Id, try as a TypeId */
	  Id *id = dynamic_cast<Id*>(code->data);

	  if (id)
	  {
	    TypeId *ti = new TypeId(&Unknown, id->name);
	    delete ti->reduce(symbols, out);
	    dtype = ti->type;
	    delete ti;
	  }
	}
      }
    }

    delete lexp;
    return new IntegerValue(dtype->size());
  }
  else
  {
    delete data->reduce(symbols, out);
    return new IntegerValue(data->type->size());
  }
}

void SizeofExp::residualize(SymbolTable *symbols)
{
  data->residualize(symbols);
}

std::string SizeofExp::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "sizeof(" << data->toString() << ")");
}

std::string SizeofExp::toTerm()
{
  return (char*)(CStr("SizeofExp(") << data->toTerm() << ")");
}

/* ************************************************************************* */

IntegerValue *SizeofType::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  delete data->reduce(symbols, out, use);

  type = &BasicInteger;
  return new IntegerValue(data->type->size());
}

void SizeofType::residualize(SymbolTable *symbols)
{
  data->residualize(symbols);
}

std::string SizeofType::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "sizeof(" << data->toString() << ")");
}

std::string SizeofType::toTerm()
{
  return (char*)(CStr("SizeofType(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *Assign::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Expression *data, *res;
  LeftExpression *lexp;
  Assign *assign;
  SymbolTable *discardsyms = symbols->spawn();
  Compound *discardout = new Compound();

  LOG(plog.debug, HDR << left->toString() << std::endl);

  lexp = left->buildStoragePath(symbols, out);
  data = right->reduce(symbols, out, AsExpression);

  if (op == AssignEq)
  {
      type = data->type;

      LOG(plog.debug, HDR << lexp->toString() << " = " << data->toString() << " (" << data << ": " << (data->known?"known":"unknown") << ")" << std::endl);

      if (data->known)
      {
	Value *value = dynamic_cast<Value*>(data);
        Expression *assigndata;

	if (!value)
	  error("Right hand side doesn't reduce to value");

	if (!value->assignments.empty())
	  error("Something reduced to a value with an assignment");

        if (value->storageIsKnown(symbols, out, true) &&
            typeid(*value) != typeid(PointerValue))
          assigndata = value->copy();
        else
          assigndata = right->reduce(discardsyms, discardout, NotUsed);

	Stat *stat = new Stat(new Assign(type, AssignEq, lexp->copy(), assigndata), true);
	out->push_back_stat(stat);

	/* *** SHOULD ADD RHS ASSIGNMENTS BECAUSE &a IS KNOWN EVEN IF
           *** a IS UNKNOWN *** */

	value->addassignment(stat);
	
	lexp->setData(symbols, out, value);
      }
      else
      {
#if 1
	/* Must be done before clearData, because lexp can be part of it */
	data->residualize(symbols);

	use = AsExpression;
	lexp->clearData(symbols, out);

	out->push_back_stat(new Stat(new Assign(type, AssignEq,	lexp->copy(), data)));

	lexp->residualize(symbols);
#else
	/* The problem with this is that, if data contains variables that are
	   out of scope once residualize is called, it doesn't work.
	   This can be resolved by not residualizing the righthand part of an
	   assignment, and adding its assignments to the lefthand part instead. */

	Value *value = type->makeValue();
	value->known = false;

	data->residualize(symbols);

	Stat *stat = new Stat(new Assign(type, AssignEq,
					 lexp->copy(),
					 data), true);

	out->push_back_stat(stat);

	value->addassignment(stat);
	
	lexp->setData(symbols, out, value);
#endif
      }
      
      res = lexp->reduce(symbols, out, use);
  }
  else
  {
    switch (op)
    {
      case AssignMul:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new Mul(&Unknown, lexp->copy(), data));
	break;
      case AssignDiv:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new Div(&Unknown, lexp->copy(), data));
	break;
      case AssignMod:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new Mod(&Unknown, lexp->copy(), data));
	break;
      case AssignPlus:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new Add(&Unknown, lexp->copy(), data));
	break;
      case AssignMin:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new Subt(&Unknown, lexp->copy(), data));
	break;
      case AssignSL:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new ShiftLeft(&Unknown, lexp->copy(), data));
	break;
      case AssignSR:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new ShiftRight(&Unknown, lexp->copy(), data));
	break;
      case AssignAnd:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new And(&Unknown, lexp->copy(), data));
	break;
      case AssignExOr:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new ExOr(&Unknown, lexp->copy(), data));
	break;
      case AssignOr:
	assign = new Assign(&Unknown, AssignEq, lexp->copy(),
			    new IncOr(&Unknown, lexp->copy(), data));
	break;
      default:
	plog.warning() << HDR << "warning: unknown assignment operator" << std::endl;
	assign = new Assign(&Unknown, AssignEq, lexp->copy(), data);
    }
	
    res = assign->reduce(symbols, out, use);
    type = res->type;
    
    delete assign;
  }
    
  delete lexp;
  delete discardsyms;
  delete discardout;

  return res;
}

void Assign::residualize(SymbolTable *symbols)
{
  left->residualize(symbols);
  right->residualize(symbols);
}

std::string Assign::toString(int indentation)
{
  typedef std::list<std::string>::iterator LI;
  std::string opstr;

  SymbolTable *symbols = new SymbolTable();
  Id *id = left->findId(symbols);
  delete symbols;

  if (!options.suppressDefinitions.empty())
    for (LI ii=options.suppressDefinitions.begin();
	 ii != options.suppressDefinitions.end(); ++ii)
      if ((*ii) == id->name)
      {
	delete id;
	return right->toString(indentation);
      }

  delete id;

  switch (op)
  {
    case AssignEq:   opstr = "="; break;
    case AssignMul:  opstr = "*="; break;
    case AssignDiv:  opstr = "/="; break;
    case AssignMod:  opstr = "%="; break;
    case AssignPlus: opstr = "+="; break;
    case AssignMin:  opstr = "-="; break;
    case AssignSL:   opstr = "<<="; break;
    case AssignSR:   opstr = ">>="; break;
    case AssignAnd:  opstr = "&="; break;
    case AssignExOr: opstr = "^="; break;
    case AssignOr:   opstr = "|="; break;
    default:        
      plog.warning() << HDR << "warning: unknown assignment operator" << std::endl;
      opstr = "?="; break;
  }

  return (char*)(CStr(indent(indentation)) << left->toString() << " " << opstr << " "
		 << right->toString());
}

std::string Assign::toTerm()
{
  std::string opstr;

  switch (op)
  {
    case AssignEq:   opstr = "AssignEq"; break;
    case AssignMul:  opstr = "AssignMul"; break;
    case AssignDiv:  opstr = "AssignDiv"; break;
    case AssignMod:  opstr = "AssignMod"; break;
    case AssignPlus: opstr = "AssignPlus"; break;
    case AssignMin:  opstr = "AssignMin"; break;
    case AssignSL:   opstr = "AssignSL"; break;
    case AssignSR:   opstr = "AssignSR"; break;
    case AssignAnd:  opstr = "AssignAnd"; break;
    case AssignExOr: opstr = "AssignExOr"; break;
    case AssignOr:   opstr = "AssignOr"; break;
    default:        
      plog.warning() << HDR << "warning: unknown assignment operator" << std::endl;
      opstr = "AssignUnknown"; break;
  }

  return (char*)(CStr("Assign(") << left->toTerm() << ", " << opstr << ", "
                                 << right->toTerm() << ")");
}

/* ************************************************************************* */

Expression *Positive::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Expression* res = data->reduce(symbols, out, use);
  type = res->type;

  if (res->known)
  {
    Value *value = dynamic_cast<Value*>(res);

    return type->makeValue(value->castToFloat());
  }
  else
    return new Positive(type, res);
}

std::string Positive::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "+" << data->toString());
}

std::string Positive::toTerm()
{
  return (char*)(CStr("Positive(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *Negative::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Expression* res = data->reduce(symbols, out, use);
  type = res->type;

  if (res->known)
  {
    Value *value = dynamic_cast<Value*>(res);

    return type->makeValue(-value->castToFloat());
  }
  else
    return new Negative(type, res);
}

std::string Negative::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "-" << data->toString());
}

std::string Negative::toTerm()
{
  return (char*)(CStr("Negative(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *Negation::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Expression* res = data->reduce(symbols, out, use);
  type = res->type;

  if (res->known)
  {
    Value *value = dynamic_cast<Value*>(res);

    return type->makeValue(!value->castToFloat());
  }
  else
    return new Negation(type, res);
}

std::string Negation::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "!(" << data->toString() << ")");
}

std::string Negation::toTerm()
{
  return (char*)(CStr("Negation(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *Tilde::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Expression* res = data->reduce(symbols, out, use);
  type = res->type;

  if (res->known)
  {
    Value *value = dynamic_cast<Value*>(res);

    plog.warning() << HDR << "warning: tilde not properly supported" << std::endl;

    return type->makeValue(~value->castToInteger());
  }
  else
    return new Tilde(type, res);
}

std::string Tilde::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "~(" << data->toString() << ")");
}

std::string Tilde::toTerm()
{
  return (char*)(CStr("Tilde(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *PreIncr::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  Assign *assign = new Assign(&Unknown, AssignEq, data->copy(),
			      new Add(&Unknown, data->copy(), new IntegerValue(1)));

  Expression *res = assign->reduce(symbols, out, use);
  delete assign;
  
  type = res->type;

  return res;
}

std::string PreIncr::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "++(" << data->toString() << ")");
}

std::string PreIncr::toTerm()
{
  return (char*)(CStr("PreIncr(") << data->toTerm() << ")");
}


/* ************************************************************************* */

Expression *PostIncr::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Subt *subt = new Subt(type,
			new Assign(&Unknown, AssignEq, data->copy(),
				   new Add(&Unknown, data->copy(), new IntegerValue(1))),
			new IntegerValue(1));

  Expression *res = subt->reduce(symbols, out, use);
  delete subt;
  
  type = res->type;

  return res;  
}

std::string PostIncr::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << data->toString() << "++");
}

std::string PostIncr::toTerm()
{
  return (char*)(CStr("PostIncr(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *PreDecr::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Assign *assign = new Assign(&Unknown, AssignEq, data->copy(),
			      new Subt(&Unknown, data->copy(), new IntegerValue(1)));

  Expression *res = assign->reduce(symbols, out, use);
  delete assign;

  type = res->type;

  return res;  
}

std::string PreDecr::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "--" << data->toString());
}

std::string PreDecr::toTerm()
{
  return (char*)(CStr("PreDecr(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression *PostDecr::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  Add *add = new Add(type,
		     new Assign(&Unknown, AssignEq, data->copy(),
				new Subt(&Unknown, data->copy(), new IntegerValue(1))),
		     new IntegerValue(1));

  Expression *res = add->reduce(symbols, out, use);
  delete add;
  
  type = res->type;

  return res;
}

std::string PostDecr::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << data->toString() << "--");
}

std::string PostDecr::toTerm()
{
  return (char*)(CStr("PostDecr(") << data->toTerm() << ")");
}

/* ************************************************************************* */

Expression* BinaryOp::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  Expression* eleft = left->reduce(symbols, out, use);
  Expression* eright = right->reduce(symbols, out, use);
  
  if (eleft->known && eright->known)
  {
    Value* vleft = dynamic_cast<Value*>(eleft);
    Value* vright = dynamic_cast<Value*>(eright);
   
    if (typeid(*eleft->type) == typeid(IntegerType) &&
	typeid(*eright->type) == typeid(IntegerType))
    {
      IntegerType *tleft = dynamic_cast<IntegerType*>(eleft->type);
      IntegerType *tright = dynamic_cast<IntegerType*>(eright->type);

      if (tleft->precision > tright->precision)
	type = tleft;
      else
	type = tright;

      IntegerValue* res = new IntegerValue(calculate(vleft->castToInteger(), 
						     vright->castToInteger()));
      
      delete eleft;
      delete eright;
      
      return res;
    }

    if ((typeid(*eleft->type) == typeid(IntegerType) ||
	 typeid(*eleft->type) == typeid(FloatType)) &&
	(typeid(*eright->type) == typeid(IntegerType) ||
	 typeid(*eright->type) == typeid(FloatType)))
    {
      FloatType *tleft = dynamic_cast<FloatType*>(eleft->type);
      FloatType *tright = dynamic_cast<FloatType*>(eright->type);

      if (tleft && tright)
      {
	if (tleft->precision > tright->precision)
	  type = tleft;
      }
      else if (tleft)
	type = tleft;
      else
	type = tright;

      FloatValue* res = new FloatValue(calculate(vleft->castToFloat(), 
						 vright->castToFloat()));

      delete eleft;
      delete eright;
      
      return res;
    }

    plog.warning() << HDR << "type '" << type->toString() << "' does not support arithmetic"
	      << std::endl;
  }
  
  type = eleft->type;
  return construct(type, eleft, eright);
}

void BinaryOp::residualize(SymbolTable *symbols)
{
  left->residualize(symbols);
  right->residualize(symbols);
}

std::string BinaryOp::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "((" << left->toString() << ")" << sign()
		 << "(" << right->toString() << "))");
}

std::string BinaryOp::toTerm()
{
  return (char*)(CStr(term()) << "(" << left->toTerm() << ", " << right->toTerm()
                              << ")");
}

/* ************************************************************************* */

Expression *Comma::reduce(SymbolTable *symbols, Compound *out, Use use)
{  
  delete left->reduce(symbols, out);

  Expression *res = right->reduce(symbols, out, use);
  type = res->type;

  return res;
}

void Comma::residualize(SymbolTable *symbols)
{
  left->residualize(symbols);
  right->residualize(symbols);
}

std::string Comma::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "(" << left->toString() << ","
		 << right->toString() << ")");
}

std::string Comma::toTerm()
{
  return (char*)(CStr("Comma(") << left->toTerm() << ", " << right->toTerm() << ")");
}

/* ************************************************************************* */

Expression* FunCall::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  typedef std::list<std::string>::iterator LI;

  Id *funid = DecodeId(id, symbols, out);
  std::string name = funid->name;
  delete funid;

  /* Check for breakpoint */
  if (!options.breakPoints.empty())
    for (LI ii=options.breakPoints.begin();
	 ii != options.breakPoints.end(); ++ii)
      if ((*ii) == name)
      {
	std::cout << "Breakpoint, " << (*ii) << std::endl;
	broken = true;
	break;
      }

  /* Check for builtin functions */
  if (name == "debug" || name == "error" || name == "reduce")
    return reduceBuiltin(name, symbols, out, use);

  /* Look up the function int the symbol table */
  Function *fun = dynamic_cast<Function*>(symbols->findSymbol(name));

  if (fun)
  {
    type = fun->data->iddecl->id->type;
  }
  else
  {
    type = &BasicInteger;
    plog.warning() << HDR << "assuming return type of " << name << " is "
		   << type->toString() << std::endl;
    
  }

  if (fun && !fun->def && (isStrategoDef(name) || type == &BasicCode))
  {
    return reduceStratego(name, symbols, out, use);
  }
  else if (!fun || !fun->def)
  {
    if (fun && !fun->def && fun->isExtern)
    {
      /* First try calling the external function, with arguments reduced as code
       * (to get ArrayValues instead of pointers) */
      FunCall *res = new FunCall(type, new Id(type, name));

      plog.debug() << HDR << "Evaluating arguments of possibly foreign " << name << std::endl;

      for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
      {
	Expression *arg = (*ii)->reduce(symbols, out, AsCode);
	res->push_back(arg);
      }

      Value *fres = callForeign(fun, res);

      if (fres)
      {
	/* Worked */
	delete res;
	return fres;
      }
      else
      {
	/* Didn't work. Fall through and residualize. */
	delete res;
      }
    }

    plog.debug() << HDR << "Evaluating arguments of " << name << std::endl;

    // Residualize unknown functions.
    FunCall *res = new FunCall(type, new Id(type, name));

    for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
    {
      Expression *arg = (*ii)->reduce(symbols, out, AsExpression);
      res->push_back(arg);

      // *** IF (POINTER) CLEARDATA ***
    }

    if (fun)
      plog.residualize() << HDR << "Residualizing call to " << name << std::endl;
    else
      plog.residualize() << HDR << "Residualizing unknown call to " << name << std::endl;

    if (fun && fun->decl)
      fun->decl->residualize(symbols);
    
    return res;
  }
  else if (typeid(*fun) == typeid(Strategy))
  {
    return reduceStrategy(name, symbols, out, use);
  }
  else
  {
    return reduceFunction(name, symbols, out, use);
  }
}

Expression* FunCall::reduceStrategy(std::string name, SymbolTable *symbols, Compound *out, Use use)
{
  Function *fun = dynamic_cast<Function*>(symbols->findSymbol(name));

  // A strategy just rewrites and returns the body
  LOG(plog.debug, HDR  << "Applying " << name << std::endl);
  
  // *** EVALUATE ARGUMENTS TO CODE ***
  Code *tempdata1, *tempdata2;
  AI ii;
  IdDecl::PI jj;
  
  tempdata1 = fun->data->data->copy();
  for (ii=arguments.begin(), jj=fun->data->iddecl->params.begin();
       ii != arguments.end() && jj != fun->data->iddecl->params.end();
       ++ii, ++jj)
  {
    // *** NON_CODE ARGUMENTS
    // *** MEMORY LEAK
    tempdata2 = tempdata1->replace((*jj)->iddecl->id->copy(), (*ii)->copy(),
				   NULL, NULL);
    delete tempdata1;
    tempdata1 = tempdata2;
  }
  
  CodeValue *cv = new CodeValue(tempdata1);
  
  return cv;
}

Expression* FunCall::reduceStratego(std::string name, SymbolTable *symbols, Compound *out, Use use)
{
  /* Calling unknown code function. Since we can't properly residualize, assume this
   * is a call to an external Stratego strategy */
  
  plog.stratego() << HDR << "calling external strategy " << name << std::endl;
  
  /* Assemble terms to pass to strategy */
  CStr terms;
  
  if (arguments.size() > 1)
    terms << "(";
  for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
  {
    Expression *term = (*ii)->reduce(symbols, out, AsCode);
    
    if (typeid(*term) == typeid(CodeValue))
      terms << dynamic_cast<CodeValue*>(term)->data->toTerm();
    else
      terms << term->toTerm();
    
    delete term;
    
    if (ii != arguments.end()-1)
      terms << ", ";
  }
  if (arguments.size() > 1)
    terms << ")";
  
  /* Run command and get input/output to it */
  struct stat statbuf;
  int filedes[2];
  
  if (stat(CStr(options.strategydir) << "/" << name, &statbuf) ||
      !S_ISREG(statbuf.st_mode) ||
      popen2(CStr(options.strategydir) << "/" << name, filedes))
  {
    LOG(plog.crawl, HDR << "Could not find fixed strategy, trying inline" << std::endl);
    
    if (popen2a(CStr(options.strategydir) << "/" << options.strategyfile,
		CStr("-s ") << name, filedes))
    {
      plog.warning() << HDR << "could not call external strategy " << name << std::endl;
      return this->copy();
    }
  }
  
  plog.stratego() << HDR << "Passing '" << terms << "'" << std::endl;
  
  /* Pass terms */
  write(filedes[1], terms, terms.str().length());
  close(filedes[1]);
  
  /* Read result */
  FILE *file = fdopen(filedes[0], "r");
  ATerm program = ATreadFromFile(file);
  fclose(file);
  
  if (!program)
    error(CStr("Failed to rewrite ") << terms.str() << " using " << name);

  plog.stratego() << HDR << "Got '" << ATwriteToString(program) << "'" << std::endl;
  
  /* Parse it */
  Code *filtered = parseATerm(program);
  CodeValue *filteredcode = dynamic_cast<CodeValue*>(filtered);

  if (!filteredcode)
    filteredcode = new CodeValue(filtered);

  if (type != &BasicCode)
  {
    Decode *decode = new Decode(type, filteredcode);

    LOG(plog.crawl, HDR << "Return value " << decode->toString() << std::endl);
    return decode->reduce(symbols, out, use);
  }
  else
  {
    LOG(plog.crawl, HDR << "Return value " << filteredcode->toString() << std::endl);
    return filteredcode;
  }
}

Expression* FunCall::reduceFunction(std::string name, SymbolTable *symbols, Compound *out, Use use)
{
  bool unknownArguments = false;
  Function *fun = dynamic_cast<Function*>(symbols->findSymbol(name));
  Id *varargparam = fun->data->iddecl->varargparam;
  
  LOG(plog.debug, HDR  << "Calling " << name << std::endl);
  
  if ((arguments.size() > fun->data->iddecl->params.size() && !varargparam) ||
      (arguments.size() < fun->data->iddecl->params.size()))
  {
    plog.warning() << HDR << "function '" << name << "' called with invalid number of arguments"
		   << std::endl;
    return new VoidValue();
  }

  // Construct stackframe
  AI ii, kk;
  IdDecl::PI jj;

  // Return value
  if (typeid(*type) != typeid(VoidType))
  {
    symbols->addReturnVariable(type);

    Declaration2 *decl = new Declaration2(new TypeSpec(type));
    decl->push_back(new IdDecl(symbols->findReturnVariable()));

    delete decl->reduce(symbols, out);
    delete decl;
  }

  Compound *fbody = out->spawn();

  bool recursive=false;
  if (symbols->getScope(name)) recursive = true;

  symbols->newFrame(name);

  Code *fundata = fun->data->data->copy();
  FunCall *res = new FunCall(type, new Id(type, name));

  for (ii=arguments.begin(), jj=fun->data->iddecl->params.begin();
       ii != arguments.end() && jj != fun->data->iddecl->params.end();
       ++ii, ++jj)
  {
    // To avoid nameclashes, rename all formal parameters
    Id *newid = getRandomId((*jj)->typespec->type);
    std::string newidname = newid->name;
    res->push_back(newid->copy());

    IdDecl *newiddecl = (*jj)->iddecl->copy();
    delete newiddecl->id;
    newiddecl->id = newid->copy();

    Declaration2 *decl = new Declaration2((*jj)->typespec->copy());
    decl->push_back(newiddecl);
    
    delete decl->reduce(symbols, fbody);
    delete decl;

    Stat *stat = new Stat(new Assign((*jj)->typespec->type, AssignEq, newid, (*ii)->copy()));

    delete stat->reduce(symbols, fbody);
    delete stat;

    TSymbol *sym = symbols->findSymbol(newidname);
    Variable *var = dynamic_cast<Variable*>(sym);

    if (!var->data->known)
    {
      plog.crawl() << HDR << "Argument " << var->toString() << " is unknown" << std::endl;

      unknownArguments = true;
    }
  }

  if (recursive && unknownArguments && !fun->data->isStatic)
  {
    // Residualize
    // *** Residualize pointers
    plog.residualize() << HDR << "Residualizing recursive call to " << name << std::endl;

    if (typeid(*type) != typeid(VoidType))
    {
      Id *retvar = symbols->findReturnVariable();
      Stat *stat = new Stat(new Assign(type, AssignEq, retvar->copy(), res));
      stat->residualize(symbols);
      fbody->push_back_stat(stat);
      symbols->popFrame();
      symbols->popReturnVariable();
      return retvar;
    }
    else
    {
      Stat *stat = new Stat(res);
      stat->residualize(symbols);
      fbody->push_back_stat(stat);
      symbols->popFrame();
      return new VoidValue();
    }
  }

  fbody = fbody->spawn();
  symbols->newFrame();

  for (ii=arguments.begin(), jj=fun->data->iddecl->params.begin(),
       kk=res->arguments.begin();
       ii != arguments.end() && jj != fun->data->iddecl->params.end();
       ++ii, ++jj, ++kk)
  {
    // Now rename them back in a new scope
    Declaration2 *decl = new Declaration2((*jj)->typespec->copy());

    decl->push_back((*jj)->iddecl->copy());
    
    delete decl->reduce(symbols, fbody);
    delete decl;

    Stat *stat = new Stat(new Assign((*jj)->typespec->type, AssignEq,
                                     dynamic_cast<Id*>((*jj)->iddecl->id)->copy(),
                                     dynamic_cast<Id*>(*kk)->copy()
                                    )
                         );

    delete stat->reduce(symbols, fbody);
    delete stat;
  }

  delete res;

  if (varargparam)
  {
    /* Declare 'code varargparam[]' */
    Declaration2 *decl = new Declaration2(new TypeSpec(&BasicCode));
    IdDecl *iddecl = new IdDecl(varargparam->copy());
    iddecl->push_back(new IntegerValue(-1));
    decl->push_back(iddecl);
	
    delete decl->reduce(symbols, fbody);
    delete decl;

    /* Gather up all superfluous arguments in there */
    for (int count=0; ii != arguments.end(); ++ii, ++count)
    {
      Expression *red = (*ii)->reduce(symbols, fbody, AsExpression);

//	plog.err() << HDR << (*ii)->toString() << " reduced to " << red->toString() << std::endl;

      if (typeid(*red) != typeid(CodeValue))
      {
//	  plog.err() << HDR << "Which isn't a CodeValue yet, so making it one now" << std::endl;
	red = new CodeValue(red);
      }

      Stat *stat = new Stat(new Assign(&BasicCode, AssignEq,
				       new ArrayIndex(&BasicCode, varargparam->copy(),
						      new IntegerValue(count)), red));

      delete stat->reduce(symbols, fbody);
      delete stat;
    }
  }

//    std::cout << "Symbol table when calling " << name << std::endl;
//    symbols->print(std::cout);

  // Reduce body
  delete fundata->reduce(symbols, fbody);
  delete fundata;

  symbols->popFrame();
  symbols->popFrame();

  if (typeid(*type) != typeid(VoidType))
  {
    Expression *retvar = symbols->findReturnVariable();
    Expression *retval = retvar->reduce(symbols, out, use);

    delete retvar;
    symbols->popReturnVariable();

    return retval;
  }
  else
    return new VoidValue();
}

Expression* FunCall::reduceBuiltin(std::string name, SymbolTable *symbols, Compound *out, Use use)
{
  if (name == "debug" || name == "error")
  {
    std::ostream *stream;
    std::ostringstream errstream;
    
    if (name == "debug")
      stream = &std::cout;
    else
      stream = &errstream;
    
    for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
    {
      Expression *term = (*ii)->reduce(symbols, out, AsExpression);
      
      (*stream) << term->toString() << " ";
      delete term;
    }

    if (name == "error")
      error(errstream.str().c_str());

    (*stream) << std::endl;

    return new VoidValue();
  }
  else if (name == "reduce")
  {
    /* OPTIONS:
     * - Remove variable generators
     * - Write out changed values
     * POSSIBLE OPTIONS:
     * - Mark all variables unknown
     * - Merge symbol tables
     */

    /* Evaluate code argument in a separate environment */
    SymbolTable *newsyms = symbols->spawn();
    Compound *newout = new Compound();
    bool resext = false;
    
    if (arguments.size() == 2)
    {
      Value *opt1 = dynamic_cast<Value*>(arguments[1]->reduce(symbols, out, AsExpression));
      
      if (!opt1 || !opt1->known)
        error("Optional argument to 'reduce' is unknown");
 
      if (opt1->castToInteger())
        resext = true;
      else
        resext = false;
    }
    else if (arguments.size() != 1)
      error("'reduce' called with invalid number of arguments");

    /* Avoid residualizing stuff in the earlier code */
    if (!resext)
      newsyms->removeGenerators();

    Code *res = arguments[0]->reduce(symbols, out, AsExpression);
    CodeValue *rescode = dynamic_cast<CodeValue*>(res);

    if (!rescode)
      error("'reduce' called with non-code argument");

    delete rescode->data->reduce(newsyms, newout);
    delete rescode;

    /* Now, residualize all symbols that were generated by the evaluated code */
    newsyms->residualizeGenerators();

    delete newsyms;   
    return new CodeValue(newout);
  }
  else
  {
    error("Unknown builtin");
    return new VoidValue();
  }
}

void FunCall::residualize(SymbolTable *symbols)
{
  Id *_id = dynamic_cast<Id*>(id);
  if (!_id)
    error("Residualized function call must have a decoded name");

  std::string name = _id->name;

  Function *fun = dynamic_cast<Function*>(symbols->findSymbol(name));

  if (fun && fun->def && fun->def->tentative)
  {
    plog.residualize() << HDR << "Residualizing definition of " << name << std::endl;
    fun->def->tentative = false;
  }

  for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
  {
    plog.residualize() << HDR << "residualizing argument " << (*ii)->toString() << " typeid "
		       << typeid(**ii).name() << std::endl;
    (*ii)->residualize(symbols);
  }
}

std::string FunCall::toString(int indentation)
{
  CStr res;

  res << indent(indentation) << id->toString() << "(";

  for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
  {
    res << (*ii)->toString();
    if (ii != arguments.end()-1)
      res << ", ";
  }

  res << ")";

  return (char*)res;
}

std::string FunCall::toTerm()
{
  CStr res;

  res << "FunCall(" << id->toTerm() << ", [";

  for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
  {
    res << (*ii)->toTerm();
    if (ii != arguments.end()-1)
      res << ", ";
  }

  res << "])";

  return (char*)res;
}

void FunCall::push_back(Expression *exp)
{
  arguments.push_back(exp);
}

/* ************************************************************************* */

Expression* IfExp::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  /* Get type */
  SymbolTable *sym = symbols->spawn();
  Compound *discard = new Compound();

  Expression *tred = truedata->reduce(sym, discard, AsExpression);
  type = tred->type;

  delete sym;
  delete discard;
  delete tred;

  /* Reduce condition, and see if we can determine its value statically */
  Expression *rcond = condition->reduce(symbols, out, AsExpression);

  if (typeid(*rcond) == typeid(IntegerValue))
  {
    IntegerValue *icond = dynamic_cast<IntegerValue*>(rcond);

    if (icond->castToInteger())
    {
      delete icond;
      return truedata->reduce(symbols, out, use);
    }
    else
    {
      delete icond;
      return falsedata->reduce(symbols, out, use);
    }
  }

  /* Reduce truedata, and see if it doesn't push anything to its output
   * (we cannot reduce an unknown if it outputs anything) */
  sym = symbols->spawn();
  discard = new Compound(true);
  tred = truedata->reduce(sym, discard, use);
  tred->residualize(sym);

  if (discard->toString() != "")
  {
    delete tred;
    tred = truedata->copy();
  }

  delete sym;
  delete discard;

  /* Do the same with falsedata */
  sym = symbols->spawn();
  discard = new Compound(true);
  Expression *fred = falsedata->reduce(sym, discard, use);
  fred->residualize(sym);

  if (discard->toString() != "")
  {
    delete fred;
    fred = falsedata->copy();
  }

  delete sym;
  delete discard;
  
  /* And finally create the reduced expression */
  return new IfExp(type, rcond, tred, fred);
}

void IfExp::residualize(SymbolTable *symbols)
{
  condition->residualize(symbols);
  truedata->residualize(symbols);
  falsedata->residualize(symbols);
}

std::string IfExp::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "((" << condition->toString() << ")?("
		 << truedata->toString() << "):(" << falsedata->toString() << "))");
}

std::string IfExp::toTerm()
{
  return (char*)(CStr("IfExp(") << condition->toTerm() << ", " << truedata->toTerm()
		 << ", " << falsedata->toTerm() << ")");
}

